package com.lollipop.synthential.algorithm;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: J.K
 * @Date: 2022-07-29 15:21
 * @Description: 最优响应算法（动态算法）
 * <p>
 * 该算法在开始前，会对服务列表中的各节点发出一个探测请求（例如 Ping 或心跳包检测），
 * 然后根据各节点的响应时间来决定由哪台服务器处理客户端请求，
 * 该算法能较好根据节点列表中每台机器的当前运行状态分发请求。
 */
@Slf4j
public class OptimalResponse {
    /**
     * 创建一个定长的线程池，用于去执行ping任务
     */
    static ExecutorService pingServerPool = Executors.newFixedThreadPool(Servers.SERVERS.size());

    public static String getServer() throws InterruptedException {
        // 创建一个CompletableFuture用于拼接任务
        CompletableFuture cfAnyOf;
        // 创建一个接收结果返回的server节点对象
        final Server resultServer = new Server();
        // 根据集群节点数量初始化一个异步任务数组
        CompletableFuture[] cfs = new CompletableFuture[Servers.SERVERS.size()];

        // 遍历整个服务器列表，为每个节点创建一个ping任务
        for (Server server : OptimalSevers.SERVERS) {
            // 获取当前节点在集群列表中的下标
            int index = Servers.SERVERS.indexOf(server);
            // 为每个节点创建一个ping任务，并交给pingServerPool线程池执行
            CompletableFuture<String> cf = CompletableFuture.supplyAsync(server::ping, pingServerPool);
            // 将创建好的异步任务加入数组中
            cfs[index] = cf;
        }

        // 将创建好的多个Ping任务组合成一个聚合任务并执行
        cfAnyOf = CompletableFuture.anyOf(cfs);

        // 监听执行完成后的回调，谁先执行完成则返回谁
        cfAnyOf.thenAccept(resultIP -> {
            log.info("最先响应检测请求的节点为：{}", resultIP);
            resultServer.setIP((String) resultIP);
        });
        //  阻塞主线程一段时间，防止CompletableFuture退出
        Thread.sleep(3000);

        // 返回最先响应检测请求（ping）的节点作为本次处理客户端请求的节点
        return resultServer.getIP();
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            log.info("第[{}]个请求，服务节点：[{}]", i, getServer());
        }
    }
}
